/*
 * JFFS2 -- Journalling Flash File System, Version 2.
 *
 * Copyright (C) 2001-2003 Free Software Foundation, Inc.
 *
 * Created by Dominic Ostrowski <dominic.ostrowski@3glab.com>
 * Contributors: David Woodhouse, Nick Garnett, Richard Panton.
 *
 * For licensing information, see the file 'LICENCE' in this directory.
 *
 * $Id: fs.c,v 1.44 2005/07/24 15:29:57 dedekind Exp $
 *
 */

#include <stdio.h>    //prife for SEEK_SET SEEK_CUR SEEK_END
#include <linux/kernel.h>
#include "nodelist.h"
#include <linux/pagemap.h>
#include <linux/crc32.h>
#include "compr.h"
#include <string.h>
#include "fcntl.h"
#include "mtd_nor.h"
#include "porting.h"
#include "asm/semaphore.h"
#include "spinor.h"
#include "mtd_partition.h"
#include "limits.h"
#include "linux/mtd/mtd_list.h"


//--------------------------------------------
jffs2_mtab_entry *jffs2_cdir_mtab_entry = NULL;
jffs2_dir jffs2_cdir_dir = JFFS2_DIR_NULL;
//==========================================================================
// Default functions

int jffs2_fileio_enosys() { return ENOSYS; }
int jffs2_fileio_erofs() { return EROFS; }
int jffs2_fileio_enoerr() { return ENOERR; }
int jffs2_fileio_enotdir() { return ENOTDIR; }

int jffs2_fileio_seltrue (struct JFFS2_FILE_TAG *fp, int which, JFFS2_ADDRWORD info)
{ return 1; }

//--------------------------------------------
//==========================================================================
// Forward definitions

// Filesystem operations
int jffs2_mount(jffs2_fstab_entry * fste, jffs2_mtab_entry * mte, int partition_num);
static int jffs2_umount(jffs2_mtab_entry * mte);
int jffs2_open(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
              int jmode, int mode,jffs2_sfile * file);
#ifdef JFFS2OPT_FS_JFFS2_WRITE
static int jffs2_ops_unlink(jffs2_mtab_entry * mte, jffs2_dir dir,
                const char *name);
static int jffs2_ops_mkdir( jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,int mode);
static int jffs2_ops_rmdir(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name);
static int jffs2_ops_rename(jffs2_mtab_entry * mte, jffs2_dir dir1,
                const char *name1, jffs2_dir dir2, const char *name2);
static int jffs2_ops_link(jffs2_mtab_entry * mte, jffs2_dir dir1, const char *name1,
              jffs2_dir dir2, const char *name2, int type);
#endif
static int jffs2_opendir(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
             jffs2_sfile * fte);
static int jffs2_chdir(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
               jffs2_dir * dir_out);
static int jffs2_stat(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
              struct jffs2_stat *buf);
static int jffs2_getinfo(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
             int key, void *buf, int len);
static int jffs2_setinfo(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
             int key, void *buf, int len);

// File operations
int jffs2_fo_read(struct JFFS2_FILE_TAG *fp, struct JFFS2_UIO_TAG *uio);
#ifdef JFFS2OPT_FS_JFFS2_WRITE
static int jffs2_fo_write(struct JFFS2_FILE_TAG *fp, struct JFFS2_UIO_TAG *uio);
#endif
static int jffs2_fo_lseek(struct JFFS2_FILE_TAG *fp, off_t * pos, int whence);
static int jffs2_fo_ioctl(struct JFFS2_FILE_TAG *fp, JFFS2_ADDRWORD com,
              JFFS2_ADDRWORD data);
static int jffs2_fo_fsync(struct JFFS2_FILE_TAG *fp, int mode);
static int jffs2_fo_close(struct JFFS2_FILE_TAG *fp);
static int jffs2_fo_fstat(struct JFFS2_FILE_TAG *fp, struct jffs2_stat *buf);
static int jffs2_fo_getinfo(struct JFFS2_FILE_TAG *fp, int key, void *buf,
                int len);
static int jffs2_fo_setinfo(struct JFFS2_FILE_TAG *fp, int key, void *buf,
                int len);

// Directory operations
static int jffs2_fo_dirread(struct JFFS2_FILE_TAG *fp, struct JFFS2_UIO_TAG *uio);
static int jffs2_fo_dirlseek(struct JFFS2_FILE_TAG *fp, off_t * pos, int whence);


static int jffs2_read_inode (struct _inode *inode);
static void jffs2_clear_inode (struct _inode *inode);
static int jffs2_truncate_file (struct _inode *inode);

//==========================================================================
// Filesystem table entries

// -------------------------------------------------------------------------
// Fstab entry.
// This defines the entry in the filesystem table.
// For simplicity we use _FILESYSTEM synchronization for all accesses since
// we should never block in any filesystem operations.

#ifdef JFFS2OPT_FS_JFFS2_WRITE
FSTAB_ENTRY(jffs2_fste, "jffs2", 0,
        JFFS2_SYNCMODE_FILE_FILESYSTEM | JFFS2_SYNCMODE_IO_FILESYSTEM,
        jffs2_mount,
        jffs2_umount,
        jffs2_open,
        jffs2_ops_unlink,
        jffs2_ops_mkdir,
        jffs2_ops_rmdir,
        jffs2_ops_rename,
        jffs2_ops_link,
        jffs2_opendir,
        jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo); /*lint !e19*/
#else
FSTAB_ENTRY(jffs2_fste, "jffs2", 0,
        JFFS2_SYNCMODE_FILE_FILESYSTEM | JFFS2_SYNCMODE_IO_FILESYSTEM,
        jffs2_mount,
        jffs2_umount,
        jffs2_open,
        (jffs2_fsop_unlink *)jffs2_fileio_erofs,
        (jffs2_fsop_mkdir *)jffs2_fileio_erofs,
        (jffs2_fsop_rmdir *)jffs2_fileio_erofs,
        (jffs2_fsop_rename *)jffs2_fileio_erofs,
        (jffs2_fsop_link *)jffs2_fileio_erofs,
        jffs2_opendir,
        jffs2_chdir, jffs2_stat, jffs2_getinfo, jffs2_setinfo);
#endif

// -------------------------------------------------------------------------
// File operations.
// This set of file operations are used for normal open files.

jffs2_s_fileops jffs2_fileops = {
    jffs2_fo_read,
#ifdef JFFS2OPT_FS_JFFS2_WRITE
    jffs2_fo_write,
#else
    (jffs2_fileop_write *) jffs2_fileio_erofs,
#endif
    jffs2_fo_lseek,
    jffs2_fo_ioctl,
    jffs2_fileio_seltrue,
    jffs2_fo_fsync,
    jffs2_fo_close,
    jffs2_fo_fstat,
    jffs2_fo_getinfo,
    jffs2_fo_setinfo
};

// -------------------------------------------------------------------------
// Directory file operations.
// This set of operations are used for open directories. Most entries
// point to error-returning stub functions. Only the read, lseek and
// close entries are functional.

jffs2_s_fileops jffs2_dirops = {
    jffs2_fo_dirread,
    (jffs2_fileop_write *) jffs2_fileio_enosys,
    jffs2_fo_dirlseek,
    (jffs2_fileop_ioctl *) jffs2_fileio_enosys,
    jffs2_fileio_seltrue,
    (jffs2_fileop_fsync *) jffs2_fileio_enosys,
    jffs2_fo_close,
    (jffs2_fileop_fstat *) jffs2_fileio_enosys,
    (jffs2_fileop_getinfo *) jffs2_fileio_enosys,
    (jffs2_fileop_setinfo *) jffs2_fileio_enosys
};


//sdd
#define JFFS_PATH_DIVIDERS  "/"
static int jffsfs_IsPathDivider(char ch)
{
    const char *str = JFFS_PATH_DIVIDERS;
    while(*str)
    {
        if(*str == ch)
        return 1;
        str++;
    }
    return 0;
}
int jffsfs_CheckPath(const char *path)
{
    int n=0;
    int divs=0;
    while(*path && n < JFFS2_NAME_MAX && divs < 100)
    {
        if(jffsfs_IsPathDivider(*path))
        {
            n=0;
            divs++;
        }
        else
            n++;
        path++;
    }
    return (*path) ? -1 : 0;
}
//sdd
//==========================================================================
// STATIC VARIABLES !!!

static unsigned char gc_buffer[PAGE_CACHE_SIZE];    //avoids malloc when user may be under memory pressure
static unsigned char n_fs_mounted = 0;  // a counter to track the number of jffs2 instances mounted

//==========================================================================
// Directory operations

struct jffs2_dirsearch {
    struct _inode *dir;        // directory to search
    const unsigned char *path;  // path to follow
    struct _inode *node;        // Node found
    const unsigned char *name;  // last name fragment used
    int namelen;            // name fragment length
    int last;            // last name in path?
};

typedef struct jffs2_dirsearch jffs2_dirsearch;

//==========================================================================
// Ref count and nlink management


// FIXME: This seems like real cruft. Wouldn't it be better just to do the
// right thing?
static void icache_evict(struct _inode *root_i, struct _inode *i)
{
    struct _inode *this = root_i;
    struct _inode *next = (struct _inode *)NULL;
    struct _inode *temp = (struct _inode *)NULL;
    struct jffs2_inode_info *f = (struct jffs2_inode_info *)NULL;
    jffsfs_Lock();
 restart:
    D2(PRINTK("icache_evict\n"));
    // If this is an absolute search path from the root,
    // remove all cached inodes with i_count of zero (these are only
    // held where needed for dotdot filepaths)

    while (this) {
        next = this->i_cache_next;
        if (this != i && this->i_count == 0) {
            struct _inode *parent = this->i_parent;

            if (this->i_cache_next)
                this->i_cache_next->i_cache_prev = this->i_cache_prev;
            if (this->i_cache_prev)
                this->i_cache_prev->i_cache_next = this->i_cache_next;
            jffs2_clear_inode(this);
            f= JFFS2_INODE_INFO(this);
            (void)destory_MUTEX(&(f->sem));
            (void)memset_s(this, sizeof(*this), 0x5a, sizeof(*this));
            temp = this;
            free(this);
            if (parent && parent != temp) {
                parent->i_count--;
                this = root_i;
                goto restart;
            }
        }
        this = next;
    }
    jffsfs_Unlock();
}

//==========================================================================
// Directory search

// -------------------------------------------------------------------------
// init_dirsearch()
// Initialize a dirsearch object to start a search

static void init_dirsearch(jffs2_dirsearch * ds,
               struct _inode *dir, const unsigned char *name)
{
    D2(PRINTK("init_dirsearch name = %s\n", name));
    D2(PRINTK("init_dirsearch dir = %x\n", dir));

    dir->i_count++;
    ds->dir = dir;
    ds->path = name;
    ds->node = dir;
    ds->name = name;
    ds->namelen = 0;
    ds->last = false;
}

// -------------------------------------------------------------------------
// find_entry()
// Search a single directory for the next name in a path and update the
// dirsearch object appropriately.

static int find_entry(jffs2_dirsearch * ds)
{
    struct _inode *dir = ds->dir;
    const unsigned char *name = ds->path;
    const unsigned char *n = name;
    char namelen = 0;
    struct _inode *d;

    D2(PRINTK("find_entry\n"));

    // check that we really have a directory
    if (!S_ISDIR(dir->i_mode))
    {
        return ENOTDIR;
    }

    // Isolate the next element of the path name.
    while (*n != '\0' && *n != '/')
    {
        n++;
        namelen++;
    }

    // Check if this is the last path element.
    while( *n == '/') n++;
    if (*n == '\0')
        ds->last = true;

    // update name in dirsearch object
    ds->name = name;
    ds->namelen = namelen;
    if (name[0] == '.')
        switch (namelen) {
        default:
            break;
        case 2:
            // Dot followed by not Dot, treat as any other name
            if (name[1] != '.')
                break;
            // Dot Dot
            // Move back up the search path
            D2(PRINTK("find_entry found ..\n"));
            ds->dir = ds->node;
            ds->node = ds->dir->i_parent;
            ds->node->i_count++;
            return ENOERR;
        case 1:
            // Dot is consumed
            D2(PRINTK("find_entry found .\n"));
            ds->node = ds->dir;
            ds->dir->i_count++;
            return ENOERR;
        }

    // Here we have the name and its length set up.
    // Search the directory for a matching entry

    D2(PRINTK("find_entry for name = %s\n", ds->path));
    d = jffs2_lookup(dir, name, namelen);
    D2(PRINTK("find_entry got dir = %x\n", d));

    if (d == NULL)
    {
        return ENOENT;
        }
    if (IS_ERR(d))
    {
        return -(int)(PTR_ERR(d));}

    // If it's a new directory inode, increase refcount on its parent
    if (S_ISDIR(d->i_mode) && !d->i_parent) {
        d->i_parent = dir;
        dir->i_count++;
    }

    // pass back the node we have found
    ds->node = d;
    return ENOERR;

}

// -------------------------------------------------------------------------
// jffs2_find()
// Main interface to directory search code. This is used in all file
// level operations to locate the object named by the pathname.

// Returns with use count incremented on both the sought object and
// the directory it was found in
static int jffs2_find(jffs2_dirsearch * d)
{
    int err;

    D2(PRINTK("jffs2_find for path =%s\n", d->path));
    // Short circuit empty paths
    if (*(d->path) == '\0') {
        d->node->i_count++;
        return ENOERR;
    }

    // iterate down directory tree until we find the object
    // we want.
    for (;;) {
        err = find_entry(d);
        if (err != ENOERR)
        {
            return err;
        }
        if (d->last)
            return ENOERR;

        /* We're done with it, although it we found a subdir that
           will have caused the refcount to have been increased */
        jffs2_iput(d->dir);

        // Update dirsearch object to search next directory.
        d->dir = d->node;
        d->path += d->namelen;
        while (*(d->path) == '/')
            d->path++;    // skip dirname separators
    }
}

//==========================================================================
// Pathconf support
// This function provides support for pathconf() and fpathconf().

static int jffs2_pathconf(struct _inode *node, struct jffs2_pathconf_info *info)
{
    int err = ENOERR;
    D2(PRINTK("jffs2_pathconf\n"));

    switch (info->name) {
    case _PC_LINK_MAX:
        info->value = LINK_MAX;
        break;

    case _PC_MAX_CANON:
        info->value = -1;    // not supported
        err = EINVAL;
        break;

    case _PC_MAX_INPUT:
        info->value = -1;    // not supported
        err = EINVAL;
        break;

    case _PC_NAME_MAX:
        info->value = JFFS2_NAME_MAX;
        break;

    case _PC_PATH_MAX:
        info->value = JFFS2_PATH_MAX;
        break;

    case _PC_PIPE_BUF:
        info->value = -1;    // not supported
        err = EINVAL;
        break;

    case _PC_ASYNC_IO:
        info->value = -1;    // not supported
        err = EINVAL;
        break;

    case _PC_CHOWN_RESTRICTED:
        info->value = -1;    // not supported
        err = EINVAL;
        break;

    case _PC_NO_TRUNC:
        info->value = 0;
        break;

    case _PC_PRIO_IO:
        info->value = 0;
        break;

    case _PC_SYNC_IO:
        info->value = 0;
        break;

    case _PC_VDISABLE:
        info->value = -1;    // not supported
        err = EINVAL;
        break;

    default:
        err = EINVAL;
        break;
    }

    return err;
}

//==========================================================================
// Filesystem operations
// -------------------------------------------------------------------------
// jffs2_mount()
// Process a mount request. This mainly creates a root for the
// filesystem.
static int jffs2_read_super(struct super_block *sb)
{
    int err;
    struct jffs2_sb_info *c;
    struct os_mtd_nor_device *device;

    c = JFFS2_SB_INFO(sb);
    device = (struct os_mtd_nor_device*)(sb->s_dev);

    /* initialize mutex lock */
    (void)init_MUTEX(&c->alloc_sem);
    (void)init_MUTEX(&c->erase_free_sem);

    /* sector size is the erase block size */
    c->sector_size = device->block_size;//device->block_size;
    c->flash_size  = (device->block_end - device->block_start + 1)* device->block_size;//(device->block_end - device->block_start)
    c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
    err = jffs2_do_mount_fs(c);

    if (err){
        (void)destory_MUTEX(&c->alloc_sem);
        (void)destory_MUTEX(&c->erase_free_sem);
        return -err;
        }
    D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));

    sb->s_root = jffs2_iget(sb, 1);

    if (IS_ERR(sb->s_root)) {
        D1(printk(KERN_WARNING "get root inode failed\n"));
        err = PTR_ERR(sb->s_root);
        sb->s_root = NULL;
        goto out_nodes;
    }
    return 0;

out_nodes:
    jffs2_free_ino_caches(c);
    jffs2_free_raw_node_refs(c);
    free(c->blocks);
    (void)destory_MUTEX(&c->alloc_sem);
    (void)destory_MUTEX(&c->erase_free_sem);

    return err;
}

struct os_mtd_nor_device jffs_part[CONFIG_MTD_PATTITION_NUM];
/* attention: both start_addr and size should be blocksize alligned. If not,
the actual start address and size won't be what you expected */

int jffs2_mount(jffs2_fstab_entry * fste, jffs2_mtab_entry * mte, int partition_num)
{

    struct super_block *jffs2_sb = NULL;
    struct jffs2_sb_info *c = NULL;
    mtd_partition *current_node = GetSpinorPartitionHead();
    LOS_DL_LIST *patition_head = NULL;
    int err;
    struct mtd_info *spinor_mtd = NULL;

    D2(PRINTK("jffs2_mount\n"));

      jffs2_sb = NULL;
    if (jffs2_sb == NULL) {
        jffs2_sb = malloc(sizeof (struct super_block));

        if (jffs2_sb == NULL)
             {
                return ENOMEM;
             }
        c = JFFS2_SB_INFO(jffs2_sb);
        (void)memset_s(jffs2_sb, sizeof (struct super_block), 0, sizeof (struct super_block));
        patition_head = &(GetSpinorPartitionHead()->node_info);
        LOS_DL_LIST_FOR_EACH_ENTRY(current_node,patition_head,mtd_partition,node_info)
        {
            if(current_node->patitionnum == partition_num)
                break;
        }
        spinor_mtd = get_mtd("spinor");
        jffs_part[partition_num].block_end = current_node->end_block;
        jffs_part[partition_num].block_size = spinor_mtd->erasesize;/*lint !e115 !e40*/
        jffs_part[partition_num].block_start = current_node->start_block;
        (void)free_mtd(spinor_mtd);
        //jffs2_sb->s_dev = t;
        jffs2_sb->jffs2_sb.mtd = current_node->mtd_info;
        jffs2_sb->s_dev = (jffs2_io_handle_t)(&jffs_part[partition_num]);
        c->inocache_list = malloc(sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
        if (c->inocache_list == NULL) {
            free(jffs2_sb);
            return ENOMEM;
        }
        (void)memset_s(c->inocache_list, sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE, 0, sizeof(struct jffs2_inode_cache *) * INOCACHE_HASHSIZE);
        if (n_fs_mounted++ == 0) {
            (void)jffs2_create_slab_caches(); // No error check, cannot fail
            (void)jffs2_compressors_init();
        }

        err = jffs2_read_super(jffs2_sb);

        if (err)
             {
                if (--n_fs_mounted == 0)
                {
                    jffs2_destroy_slab_caches();
                    (void)jffs2_compressors_exit();
                }

                free(jffs2_sb);
                free(c->inocache_list);
                c->inocache_list = NULL;
                return err;
             }
        jffs2_sb->s_root->i_parent = jffs2_sb->s_root;    // points to itself, no dotdot paths above mountpoint
        jffs2_sb->s_root->i_cache_prev = NULL;    // root inode, so always null
        jffs2_sb->s_root->i_cache_next = NULL;
        jffs2_sb->s_root->i_count = 1;    // Ensures the root inode is always in ram until umount

#ifdef JFFS2OPT_FS_JFFS2_GCTHREAD
        jffs2_start_garbage_collect_thread(c);
#endif
    }
    mte->data = (JFFS2_ADDRWORD) jffs2_sb;
    jffs2_sb->s_mount_count++;
    mte->root = (jffs2_dir) jffs2_sb->s_root;
    D2(PRINTK("jffs2_mounted superblock at %x\n", mte->root));

    return ENOERR;
}

extern jffs2_dir jffs2_cdir_dir;
extern jffs2_mtab_entry *jffs2_cdir_mtab_entry;

// -------------------------------------------------------------------------
// jffs2_umount()
// Unmount the filesystem.

static int jffs2_umount(jffs2_mtab_entry * mte)
{
    struct _inode *root = (struct _inode *) mte->root;
    struct super_block *jffs2_sb = root->i_sb;
    struct jffs2_sb_info *c = JFFS2_SB_INFO(jffs2_sb);
        struct jffs2_full_dirent *fd, *next;

    D2(PRINTK("jffs2_umount\n"));

    // Only really umount if this is the only mount
    if (jffs2_sb->s_mount_count == 1) {
        icache_evict(root, NULL);
        if (root->i_cache_next != NULL)    {
            struct _inode *inode = root;
            PRINTK("Refuse to unmount.\n");
            while (inode) {
                PRINTK("Ino #%u has use count %d\n",
                       inode->i_ino, inode->i_count);
                inode = inode->i_cache_next;
            }
            // root icount was set to 1 on mount
            return EBUSY;
                }
        if (root->i_count == 2 &&
            jffs2_cdir_mtab_entry == mte &&
            jffs2_cdir_dir == (jffs2_dir)root &&
            !strcmp(mte->name, "/")) {
            /* If we were mounted on root, there's no
               way for the cwd to change out and free
               the file system for unmounting. So we hack
               it -- if cwd is '/' we unset it. Perhaps
               we should allow chdir(NULL) to unset
               jffs2_cdir_dir? */
            jffs2_cdir_dir = JFFS2_DIR_NULL;
            jffs2_iput(root);
        }
        /* Argh. The fileio code sets this; never clears it */
        if (jffs2_cdir_mtab_entry == mte)
            jffs2_cdir_mtab_entry = NULL;

        if (root->i_count != 1) {
            PRINTK("Ino #1 has use count %d\n",
                   root->i_count);
            return EBUSY;
        }
#ifdef JFFS2OPT_FS_JFFS2_GCTHREAD
        jffs2_stop_garbage_collect_thread(c);
#endif
        jffs2_iput(root);    // Time to free the root inode

        // free directory entries
        for (fd = root->jffs2_i.dents; fd; fd = next) {
          next=fd->next;
          jffs2_free_full_dirent(fd);
        }

        (void)destory_MUTEX(&root->jffs2_i);
        free(root);

        // Clean up the super block and root inode
        jffs2_free_ino_caches(c);
        jffs2_free_raw_node_refs(c);
        free(c->blocks);
        c->blocks = NULL;
        free(c->inocache_list);
        c->inocache_list = NULL;

        (void)destory_MUTEX(&c->alloc_sem);
        (void)destory_MUTEX(&c->erase_free_sem);
        free(jffs2_sb);
        // Clear superblock & root pointer
        mte->root = JFFS2_DIR_NULL;
        mte->data = 0;
        mte->fs->data = 0;    // fstab entry, visible to all mounts. No current mount
        // That's all folks.
        D2(PRINTK("jffs2_umount No current mounts\n"));
    } else {
        jffs2_sb->s_mount_count--;
        }
    if (--n_fs_mounted == 0) {
        jffs2_destroy_slab_caches();
        (void)jffs2_compressors_exit();
    }
    return ENOERR;
}

// -------------------------------------------------------------------------
// jffs2_open()
// Open a file for reading or writing.

int jffs2_open(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
              int jmode, int mode,jffs2_sfile * file)
{
    jffs2_dirsearch ds;
    struct _inode *node = NULL;
    int err;
    D2(PRINTK("jffs2_open\n"));

    /* If no chdir has been called and we were the first file system
       mounted, we get called with dir == NULL. Deal with it */
    if (!dir)
        dir = mte->root;
#ifndef JFFS2OPT_FS_JFFS2_WRITE
    if (jmode & (JFFS2_O_CREAT|JFFS2_O_TRUNC|JFFS2_O_WRONLY))
        return EROFS;
#endif

    if(jffsfs_CheckPath(name))
        return ENAMETOOLONG;

    init_dirsearch(&ds, (struct _inode *) dir,
                       (const unsigned char *) name);

    err = jffs2_find(&ds);

    if (err == ENOENT) {
#ifdef JFFS2OPT_FS_JFFS2_WRITE
        if (ds.last && (jmode & JFFS2_O_CREAT)) {

            // No node there, if the O_CREAT bit is set then we must
            // create a new one. The dir and name fields of the dirsearch
            // object will have been updated so we know where to put it.
            err = jffs2_create(ds.dir, ds.name, mode, &node);
        if (err != 0)
        {
            //Possible orphaned inode on the flash - but will be gc'd
            jffs2_iput(ds.dir);
            return -err;
        }

        err = ENOERR;
        }
#endif
    } else if (err == ENOERR) {
        // The node exists. If the O_CREAT and O_EXCL bits are set, we
        // must fail the open.

        if ((jmode & (JFFS2_O_CREAT |JFFS2_O_EXCL)) == (JFFS2_O_CREAT |JFFS2_O_EXCL)) {
            jffs2_iput(ds.node);
            err = EEXIST;
        } else
            node = ds.node;
    }

    // Finished with the directory now
    jffs2_iput(ds.dir);

    if (err != ENOERR)
    {
        return err;
    }
    // Check that we actually have a file here
    if (S_ISDIR(node->i_mode)) /*lint !e613*/
    {
        jffs2_iput(node);
        return EISDIR;
    }

    // If the O_TRUNC bit is set we must clean out the file data.
    if (jmode & JFFS2_O_TRUNC) {
#ifdef JFFS2OPT_FS_JFFS2_WRITE
             err = jffs2_truncate_file(node);
             if (err) {
                  jffs2_iput(node);
                  return err;
             }
#else
             jffs2_iput(node);
             return EROFS;
#endif
        }

    // Initialise the file object
    file->f_flag = jmode & JFFS2_FILE_MODE_MASK;
    file->f_type = JFFS2_FILE_TYPE_FILE;
    file->f_ops = &jffs2_fileops;
    file->f_offset = (jmode & JFFS2_O_APPEND) ? node->i_size : 0; /*lint !e613*/
    file->f_data = (JFFS2_ADDRWORD) node;
    file->f_xops = 0;

    return ENOERR;
}

#ifdef JFFS2OPT_FS_JFFS2_WRITE
// -------------------------------------------------------------------------
// jffs2_ops_unlink()
// Remove a file link from its directory.

static int jffs2_ops_unlink(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name)
{
    jffs2_dirsearch ds;
    int err;

    D2(PRINTK("jffs2_ops_unlink\n"));

    if(jffsfs_CheckPath(name))
        return ENAMETOOLONG;

    init_dirsearch(&ds, (struct _inode *) dir,
                       (const unsigned char *)name);

    err = jffs2_find(&ds);

    if (err != ENOERR) {
        jffs2_iput(ds.dir);
        return err;
    }

    // Cannot unlink directories, use rmdir() instead
    if (S_ISDIR(ds.node->i_mode)) {
        jffs2_iput(ds.dir);
        jffs2_iput(ds.node);
        return EISDIR;
    }

    // Delete it from its directory

    err = jffs2_unlink(ds.dir, ds.node, ds.name);
    jffs2_iput(ds.dir);
    jffs2_iput(ds.node);

    return -err;
}

// -------------------------------------------------------------------------
// jffs2_ops_mkdir()
// Create a new directory.

static int jffs2_ops_mkdir( jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,int mode)
{
    jffs2_dirsearch ds;
    int err;
    D2(PRINTK("jffs2_ops_mkdir\n"));

    if(jffsfs_CheckPath(name))
        return ENAMETOOLONG;

    init_dirsearch(&ds, (struct _inode *) dir,
                       (const unsigned char *)name);

    err = jffs2_find(&ds);
    if (err == ENOENT) {
        if (ds.last) {
            // The entry does not exist, and it is the last element in
            // the pathname, so we can create it here.
        err = jffs2_mkdir(ds.dir, ds.name, mode);
        }
        // If this was not the last element, then an intermediate
        // directory does not exist.
    } else {
        // If there we no error, something already exists with that
        // name, so we cannot create another one.
               if (err == ENOERR) {
                jffs2_iput(ds.node);
                err = -EEXIST;
               }
    }
    jffs2_iput(ds.dir);
    return err;
}

// -------------------------------------------------------------------------
// jffs2_ops_rmdir()
// Remove a directory.

static int jffs2_ops_rmdir(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name)
{
    jffs2_dirsearch ds;
    int err;

    D2(PRINTK("jffs2_ops_rmdir\n"));

    if(jffsfs_CheckPath(name))
        return ENAMETOOLONG;

    init_dirsearch(&ds, (struct _inode *) dir,
                       (const unsigned char *)name);

    err = jffs2_find(&ds);
    if (err != ENOERR) {
        jffs2_iput(ds.dir);
        return -err;
    }

    // Check that this is actually a directory.
    if (!S_ISDIR(ds.node->i_mode)) {
        jffs2_iput(ds.dir);
        jffs2_iput(ds.node);
        return -ENOTDIR;
    }

    err = jffs2_rmdir(ds.dir, ds.node, ds.name);

    jffs2_iput(ds.dir);
    jffs2_iput(ds.node);
    return err;
}

// -------------------------------------------------------------------------
// jffs2_ops_rename()
// Rename a file/dir.

static int jffs2_ops_rename(jffs2_mtab_entry * mte, jffs2_dir dir1,
                const char *name1, jffs2_dir dir2, const char *name2)
{
    jffs2_dirsearch ds1, ds2;
    int err;
    struct jffs2_inode_info *f = (struct jffs2_inode_info *)NULL;
    struct jffs2_full_dirent *fd = (struct jffs2_full_dirent *)NULL;
    unsigned int oldrelpath_len, newrelpath_len, cmp_namelen;
    D2(PRINTK("jffs2_ops_rename\n"));

    if(jffsfs_CheckPath(name1))
        return ENAMETOOLONG;

    if(jffsfs_CheckPath(name2))
        return ENAMETOOLONG;

    oldrelpath_len = strlen(name1);
    newrelpath_len = strlen(name2);
    if(!oldrelpath_len || !newrelpath_len)
     return EPERM;

    cmp_namelen =(oldrelpath_len <=newrelpath_len)? oldrelpath_len : newrelpath_len ;
    if((!strncmp(name1,name2,cmp_namelen)) && ((name1[cmp_namelen]=='/') ||(name2[cmp_namelen]=='/')))
    {
        err = -EPERM;
        return err;
    }

    init_dirsearch(&ds1, (struct _inode *) dir1,
                       (const unsigned char *)name1);

    err = jffs2_find(&ds1);

    if (err != ENOERR) {
        jffs2_iput(ds1.dir);
        return err;
    }


    init_dirsearch(&ds2, (struct _inode *) dir2,
                       (const unsigned char *)name2);

    err = jffs2_find(&ds2);

    // Allow through renames to non-existent objects.
    if (ds2.last && err == ENOENT) {
        ds2.node = NULL;
        err = ENOERR;
    }

    else if((err == ENOERR) && S_ISDIR(ds2.node->i_mode))
    {
        f= JFFS2_INODE_INFO(ds2.node);
        for (fd = f->dents ; fd; fd = fd->next) {
            if (fd->ino)
            {
                jffs2_iput(ds2.node);
                err = ENOTEMPTY;
                break;
            }
        }
    }
    if (err != ENOERR) {
        jffs2_iput(ds1.dir);
        jffs2_iput(ds1.node);
        jffs2_iput(ds2.dir);
        return err;
    }

    // Null rename, just return
    if (ds1.node == ds2.node) {
        err = ENOERR;
        goto out;
    }

    // First deal with any entry that is at the destination
    if (ds2.node != NULL) {
        // Check that we are renaming like-for-like

        if (!S_ISDIR(ds1.node->i_mode) && S_ISDIR(ds2.node->i_mode)) {
            err = EISDIR;
            goto out;
        }

        if (S_ISDIR(ds1.node->i_mode) && !S_ISDIR(ds2.node->i_mode)) {
            err = ENOTDIR;
            goto out;
        }

        // Now delete the destination directory entry
        /* Er, what happened to atomicity of rename()? */
        err = -jffs2_unlink(ds2.dir, ds2.node, ds2.name);

        if (err != 0)
            goto out;

    }
    // Now we know that there is no clashing node at the destination,
    // make a new direntry at the destination and delete the old entry
    // at the source.

    err = -jffs2_rename(ds1.dir, ds1.node, ds1.name, ds2.dir, ds2.name);

    // Update directory times
    if (!err)
        ds1.dir->i_ctime =
            ds1.dir->i_mtime =
            ds2.dir->i_ctime = ds2.dir->i_mtime = jffs2_get_timestamp();
 out:
    jffs2_iput(ds1.dir);
    if (S_ISDIR(ds1.node->i_mode)) {
        /* Renamed a directory to elsewhere... so fix up its
           i_parent pointer and the i_counts of its old and
           new parents. */
        jffs2_iput(ds1.node->i_parent);
        ds1.node->i_parent = ds2.dir;
        /* We effectively increase its use count by not... */
    } else {
        jffs2_iput(ds2.dir); /* ... doing this */
    }
    jffs2_iput(ds1.node);
    if (ds2.node != NULL)
        jffs2_iput(ds2.node);

    return err;
}

// -------------------------------------------------------------------------
// jffs2_ops_link()
// Make a new directory entry for a file.

static int jffs2_ops_link(jffs2_mtab_entry * mte, jffs2_dir dir1, const char *name1,
              jffs2_dir dir2, const char *name2, int type)
{
    jffs2_dirsearch ds1, ds2;
    int err;

    D2(PRINTK("jffs2_ops_link\n"));

    // Only do hard links for now in this filesystem
    if (type != JFFS2_FSLINK_HARD)
        return EINVAL;

    if(jffsfs_CheckPath(name1))
        return ENAMETOOLONG;

    init_dirsearch(&ds1, (struct _inode *) dir1,
                       (const unsigned char *) name1);

    err = jffs2_find(&ds1);

    if (err != ENOERR) {
        jffs2_iput(ds1.dir);
        return err;
    }

    if(jffsfs_CheckPath(name2))
    {
        jffs2_iput(ds1.dir);
        jffs2_iput(ds1.node);
        return ENAMETOOLONG;
    }

    init_dirsearch(&ds2, (struct _inode *) dir2,
                       (const unsigned char *) name2);

    err = jffs2_find(&ds2);

    // Don't allow links to existing objects
    if (err == ENOERR) {
        jffs2_iput(ds1.dir);
        jffs2_iput(ds1.node);
        jffs2_iput(ds2.dir);
        jffs2_iput(ds2.node);
        return EEXIST;
    }

    // Allow through links to non-existing terminal objects
    if (ds2.last && err == ENOENT) {
        ds2.node = NULL;
        err = ENOERR;
    }

    if (err != ENOERR) {
        jffs2_iput(ds1.dir);
        jffs2_iput(ds1.node);
        jffs2_iput(ds2.dir);
        return err;
    }

    // Now we know that there is no existing node at the destination,
    // make a new direntry at the destination.

    err = jffs2_link(ds1.node, ds2.dir, ds2.name);

    if (err == 0)
        ds1.node->i_ctime =
            ds2.dir->i_ctime = ds2.dir->i_mtime = jffs2_get_timestamp();

    jffs2_iput(ds1.dir);
    jffs2_iput(ds1.node);
    jffs2_iput(ds2.dir);

    return -err;
}
#endif /* JFFS2OPT_FS_JFFS2_WRITE */
// -------------------------------------------------------------------------
// jffs2_opendir()
// Open a directory for reading.

static int jffs2_opendir(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
             jffs2_sfile * file)
{
    jffs2_dirsearch ds;
    int err;

    D2(PRINTK("jffs2_opendir\n"));

    if(jffsfs_CheckPath(name))
        return ENAMETOOLONG;

    init_dirsearch(&ds, (struct _inode *) dir,
                       (const unsigned char *) name);

    err = jffs2_find(&ds);

    jffs2_iput(ds.dir);

    if (err != ENOERR){
        return err;}

    // check it is really a directory.
    if (!S_ISDIR(ds.node->i_mode)) {
        jffs2_iput(ds.node);
        return ENOTDIR;
    }

    // Initialize the file object, setting the f_ops field to a
    // special set of file ops.

    file->f_flag = 0;
    file->f_type = JFFS2_FILE_TYPE_FILE;
    file->f_ops = &jffs2_dirops;
    file->f_offset = 0;
    file->f_data = (JFFS2_ADDRWORD) ds.node;
    file->f_xops = 0;

    return ENOERR;

}

// -------------------------------------------------------------------------
// jffs2_chdir()
// Change directory support.

static int jffs2_chdir(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
               jffs2_dir * dir_out)
{
    D2(PRINTK("jffs2_chdir\n"));

    if (dir_out != NULL) {
        // This is a request to get a new directory pointer in
        // *dir_out.

        jffs2_dirsearch ds;
        int err;

        if(jffsfs_CheckPath(name))
            return ENAMETOOLONG;

        init_dirsearch(&ds, (struct _inode *) dir,
                               (const unsigned char *) name);

        err = jffs2_find(&ds);
        jffs2_iput(ds.dir);

        if (err != ENOERR)
            return err;

        // check it is a directory
        if (!S_ISDIR(ds.node->i_mode)) {
            jffs2_iput(ds.node);
            return ENOTDIR;
        }

        // Pass it out
        *dir_out = (jffs2_dir) ds.node;
    } else {
        // If no output dir is required, this means that the mte and
        // dir arguments are the current cdir setting and we should
        // forget this fact.

        struct _inode *node = (struct _inode *) dir;

        // Just decrement directory reference count.
        jffs2_iput(node);
    }

    return ENOERR;
}

// -------------------------------------------------------------------------
// jffs2_stat()
// Get struct stat info for named object.

static int jffs2_stat(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
              struct jffs2_stat *buf)
{
    jffs2_dirsearch ds;
    int err;

    D2(PRINTK("jffs2_stat\n"));

    if(jffsfs_CheckPath(name))
        return ENAMETOOLONG;

    init_dirsearch(&ds, (struct _inode *) dir,
                       (const unsigned char *) name);

    err = jffs2_find(&ds);
    jffs2_iput(ds.dir);

    if (err != ENOERR)
        return err;

    // Fill in the status
    buf->st_mode = ds.node->i_mode;
    buf->st_ino = ds.node->i_ino;
    buf->st_dev = 0;
    buf->st_nlink = ds.node->i_nlink;
    buf->st_uid = ds.node->i_uid;
    buf->st_gid = ds.node->i_gid;
    buf->st_size = ds.node->i_size;
    buf->sddst_atime = ds.node->i_atime;
    buf->sddst_mtime = ds.node->i_mtime;
    buf->sddst_ctime = ds.node->i_ctime;

    jffs2_iput(ds.node);

    return ENOERR;
}

// -------------------------------------------------------------------------
// jffs2_getinfo()
// Getinfo. Currently only support pathconf().

static int jffs2_getinfo(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
             int key, void *buf, int len)
{
    jffs2_dirsearch ds;
    int err;

    D2(PRINTK("jffs2_getinfo\n"));

    if(jffsfs_CheckPath(name))
        return ENAMETOOLONG;

    init_dirsearch(&ds, (struct _inode *) dir,
                       (const unsigned char *) name);

    err = jffs2_find(&ds);
    jffs2_iput(ds.dir);

    if (err != ENOERR)
        return err;

    switch (key) {
    case FS_INFO_CONF:
        err = jffs2_pathconf(ds.node, (struct jffs2_pathconf_info *) buf);
        break;

    default:
        err = EINVAL;
    }

    jffs2_iput(ds.node);
    return err;
}

// -------------------------------------------------------------------------
// jffs2_setinfo()
// Setinfo. Nothing to support here at present.

static int jffs2_setinfo(jffs2_mtab_entry * mte, jffs2_dir dir, const char *name,
             int key, void *buf, int len)
{
    // No setinfo keys supported at present

    D2(PRINTK("jffs2_setinfo\n"));

    return EINVAL;
}

//==========================================================================
// File operations

// -------------------------------------------------------------------------
// jffs2_fo_read()
// Read data from the file.

int jffs2_fo_read(struct JFFS2_FILE_TAG *fp, struct JFFS2_UIO_TAG *uio)
{
    struct _inode *inode = (struct _inode *) fp->f_data;
    struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
    struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
    int i;
    ssize_t resid = uio->uio_resid;
    off_t pos = fp->f_offset;

    (void)down(&f->sem);

    // Loop over the io vectors until there are none left
    for (i = 0; i < uio->uio_iovcnt && pos < inode->i_size; i++) {
        int ret;
        jffs2_iovec *iov = &uio->uio_iov[i];
        off_t len = min((iov->iov_len), (inode->i_size - pos));

        D2(PRINTK("jffs2_fo_read inode size %d\n", inode->i_size));

        ret =
            jffs2_read_inode_range(c, f,
                       (unsigned char *) iov->iov_base, pos,
                       len);
        if (ret) {
            D1(PRINTK
               ("jffs2_fo_read(): read_inode_range failed %d\n",
                ret));
            uio->uio_resid = resid;
            (void)up(&f->sem);
            return -ret;
        }
        resid -= len;
        pos += len;
    }

    // We successfully read some data, update the node's access time
    // and update the file offset and transfer residue.

    inode->i_atime = jffs2_get_timestamp();

    uio->uio_resid = resid;
    fp->f_offset = pos;

    (void)up(&f->sem);

    return ENOERR;
}


#ifdef JFFS2OPT_FS_JFFS2_WRITE
// -------------------------------------------------------------------------
// jffs2_fo_write()
// Write data to file.
static int jffs2_extend_file (struct _inode *inode, struct jffs2_raw_inode *ri,
               unsigned long offset)
{
    struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
    struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
    struct jffs2_full_dnode *fn;
    uint32_t phys_ofs, alloc_len;
    int ret = 0;

    /* Make new hole frag from old EOF to new page */
    D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
          (unsigned int)inode->i_size, offset));

    ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloc_len, ALLOC_NORMAL);
    if (ret)
        return ret;

    (void)down(&f->sem);

    ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
    ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
    ri->totlen = cpu_to_je32(sizeof(*ri));
    ri->hdr_crc = cpu_to_je32(crc32(0, ri, (sizeof(struct jffs2_unknown_node)-4)));

    ri->version = cpu_to_je32(++f->highest_version);
    ri->isize = cpu_to_je32(max((uint32_t)inode->i_size, offset));

    ri->offset = cpu_to_je32(inode->i_size);
    ri->dsize = cpu_to_je32((offset - inode->i_size));
    ri->csize = cpu_to_je32(0);
    ri->compr = JFFS2_COMPR_ZERO;
    ri->node_crc = cpu_to_je32(crc32(0, ri, (sizeof(*ri)-8)));
    ri->data_crc = cpu_to_je32(0);

    fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
    jffs2_complete_reservation(c);
    if (IS_ERR(fn)) {
        ret = PTR_ERR(fn);
        (void)up(&f->sem);
        return ret;
    }
    ret = jffs2_add_full_dnode_to_inode(c, f, fn);
    if (f->metadata) {
        jffs2_mark_node_obsolete(c, f->metadata->raw);
        jffs2_free_full_dnode(f->metadata);
        f->metadata = NULL;
    }
    if (ret) {
        D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
        jffs2_mark_node_obsolete(c, fn->raw);
        jffs2_free_full_dnode(fn);
        (void)up(&f->sem);
        return ret;
    }
    inode->i_size = offset;
    (void)up(&f->sem);
    return 0;
}

// jffs2_fo_open()
// Truncate a file
static int jffs2_truncate_file (struct _inode *inode)
{
     struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); /*lint !e613*/
     struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); /*lint !e613*/
     struct jffs2_full_dnode *new_metadata, * old_metadata;
     struct jffs2_raw_inode *ri;
     uint32_t phys_ofs, alloclen;
     int err;

     ri = jffs2_alloc_raw_inode();
     if (!ri) {
          return ENOMEM;
     }
     err = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);

     if (err) {
          jffs2_free_raw_inode(ri);
          return err;
     }
     (void)down(&f->sem);
     ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
     ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
     ri->totlen = cpu_to_je32(sizeof(*ri));
     ri->hdr_crc = cpu_to_je32(crc32(0, ri, (sizeof(struct jffs2_unknown_node)-4)));

     ri->ino = cpu_to_je32(inode->i_ino);
     ri->version = cpu_to_je32(++f->highest_version);

     ri->uid = cpu_to_je16(inode->i_uid);
     ri->gid = cpu_to_je16(inode->i_gid);
     ri->mode = cpu_to_jemode(inode->i_mode);
     ri->isize = cpu_to_je32(0);
     ri->atime = cpu_to_je32(inode->i_atime);
     ri->mtime = cpu_to_je32(jffs2_get_timestamp());
     ri->offset = cpu_to_je32(0);
     ri->csize = ri->dsize = cpu_to_je32(0);
     ri->compr = JFFS2_COMPR_NONE;
     ri->node_crc = cpu_to_je32(crc32(0, ri, (sizeof(*ri)-8)));
     ri->data_crc = cpu_to_je32(0);
     new_metadata = jffs2_write_dnode(c, f, ri, NULL, 0,
                                      phys_ofs, ALLOC_NORMAL);
     if (IS_ERR(new_metadata)) {
          jffs2_complete_reservation(c);
          jffs2_free_raw_inode(ri);
          (void)up(&f->sem);
          return PTR_ERR(new_metadata);
     }

     /* It worked. Update the inode */
     inode->i_mtime = jffs2_get_timestamp(); /*lint !e613*/
     inode->i_size = 0; /*lint !e613*/
     old_metadata = f->metadata;
     jffs2_truncate_fragtree (c, &f->fragtree, 0);
     f->metadata = new_metadata;
     if (old_metadata) {
          jffs2_mark_node_obsolete(c, old_metadata->raw);
          jffs2_free_full_dnode(old_metadata);
     }
     jffs2_free_raw_inode(ri);

     (void)up(&f->sem);
     jffs2_complete_reservation(c);

     return 0;
}



static int jffs2_fo_write(struct JFFS2_FILE_TAG *fp, struct JFFS2_UIO_TAG *uio)
{
    struct _inode *inode = (struct _inode *) fp->f_data;
    off_t pos = fp->f_offset;
    ssize_t resid = uio->uio_resid;
    struct jffs2_raw_inode ri;
    struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
    struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
    int i;

    // If the APPEND mode bit was supplied, force all writes to
    // the end of the file.
    if (fp->f_flag & JFFS2_FAPPEND)
        pos = fp->f_offset = inode->i_size;

    if (pos < 0)
        return EINVAL;

    (void)memset_s(&ri, sizeof(ri), 0, sizeof(ri));

    ri.ino = cpu_to_je32(f->inocache->ino);
    ri.mode = cpu_to_jemode(inode->i_mode);
    ri.uid = cpu_to_je16(inode->i_uid);
    ri.gid = cpu_to_je16(inode->i_gid);
    ri.atime = ri.ctime = ri.mtime = cpu_to_je32(jffs2_get_timestamp());

    if (pos > inode->i_size) {
        int err;
        ri.version = cpu_to_je32(++f->highest_version);
        err = jffs2_extend_file(inode, &ri, pos);
        if (err)
            return -err;
    }
    ri.isize = cpu_to_je32(inode->i_size);

    // Now loop over the iovecs until they are all done, or
    // we get an error.
    for (i = 0; i < uio->uio_iovcnt; i++) {
        jffs2_iovec *iov = &uio->uio_iov[i];
        unsigned char *buf = iov->iov_base;
        off_t len = iov->iov_len;

        uint32_t writtenlen;
        int err;

        D2(PRINTK("jffs2_fo_write page_start_pos %d\n", pos));
        D2(PRINTK("jffs2_fo_write transfer size %d\n", len));

        err = jffs2_write_inode_range(c, f, &ri, buf,
                          pos, len, &writtenlen);
        if (err)
        {
            pos += writtenlen;
            resid -= writtenlen;

            inode->i_mtime = inode->i_ctime = je32_to_cpu(ri.mtime);
            if (pos > inode->i_size)
                inode->i_size = pos;

            uio->uio_resid = resid;
            fp->f_offset = pos;

            return -err;
        }

        if (writtenlen != len)
        {
            pos += writtenlen;
            resid -= writtenlen;

            inode->i_mtime = inode->i_ctime = je32_to_cpu(ri.mtime);
            if (pos > inode->i_size)
                inode->i_size = pos;

            uio->uio_resid = resid;
            fp->f_offset = pos;

            return ENOSPC;
        }

        pos += len;
        resid -= len;
    }

    // We wrote some data successfully, update the modified and access
    // times of the inode, increase its size appropriately, and update
    // the file offset and transfer residue.
    inode->i_mtime = inode->i_ctime = je32_to_cpu(ri.mtime);
    if (pos > inode->i_size)
        inode->i_size = pos;

    uio->uio_resid = resid;
    fp->f_offset = pos;

    return ENOERR;
}
#endif /* JFFS2OPT_FS_JFFS2_WRITE */

// -------------------------------------------------------------------------
// jffs2_fo_lseek()
// Seek to a new file position.

static int jffs2_fo_lseek(struct JFFS2_FILE_TAG *fp, off_t * apos, int whence)
{
    struct _inode *node = (struct _inode *) fp->f_data;
    off_t pos = *apos;

    D2(PRINTK("jffs2_fo_lseek\n"));

    switch (whence) {
    case SEEK_SET:
        // Pos is already where we want to be.
        break;

    case SEEK_CUR:
        // Add pos to current offset.
        pos += fp->f_offset;
        break;

    case SEEK_END:
        // Add pos to file size.
        pos += node->i_size;
        break;

    default:
        return EINVAL;
    }

        if (pos < 0 )
                return EINVAL;

    // All OK, set fp offset and return new position.
    *apos = fp->f_offset = pos;

    return ENOERR;
}

// -------------------------------------------------------------------------
// jffs2_fo_ioctl()
// Handle ioctls. Currently none are defined.

static int jffs2_fo_ioctl(struct JFFS2_FILE_TAG *fp, JFFS2_ADDRWORD com,
              JFFS2_ADDRWORD data)
{
    // No Ioctls currenly defined.

    D2(PRINTK("jffs2_fo_ioctl\n"));

    return EINVAL;
}

// -------------------------------------------------------------------------
// jffs2_fo_fsync().
// Force the file out to data storage.

static int jffs2_fo_fsync(struct JFFS2_FILE_TAG *fp, int mode)
{
    // Data is always permanently where it belongs, nothing to do
    // here.

    D2(PRINTK("jffs2_fo_fsync\n"));

    return ENOERR;
}

// -------------------------------------------------------------------------
// jffs2_fo_close()
// Close a file. We just decrement the refcnt and let it go away if
// that is all that is keeping it here.

static int jffs2_fo_close(struct JFFS2_FILE_TAG *fp)
{
    struct _inode *node = (struct _inode *) fp->f_data;

    D2(PRINTK("jffs2_fo_close\n"));
    if(node==NULL)
    {
        return ENOENT;
    }
    jffs2_iput(node);

    fp->f_data = 0;
    free(fp);

    return ENOERR;
}

// -------------------------------------------------------------------------
//jffs2_fo_fstat()
// Get file status.

static int jffs2_fo_fstat(struct JFFS2_FILE_TAG *fp, struct jffs2_stat *buf)
{
    struct _inode *node = (struct _inode *) fp->f_data;

    D2(PRINTK("jffs2_fo_fstat\n"));

    // Fill in the status
    buf->st_mode = node->i_mode;
    buf->st_ino = node->i_ino;
    buf->st_dev = 0;
    buf->st_nlink = node->i_nlink;
    buf->st_uid = node->i_uid;
    buf->st_gid = node->i_gid;
    buf->st_size = node->i_size;
    buf->sddst_atime = node->i_atime;
    buf->sddst_mtime = node->i_mtime;
    buf->sddst_ctime = node->i_ctime;

    return ENOERR;
}

// -------------------------------------------------------------------------
// jffs2_fo_getinfo()
// Get info. Currently only supports fpathconf().

static int jffs2_fo_getinfo(struct JFFS2_FILE_TAG *fp, int key, void *buf,
                int len)
{
    struct _inode *node = (struct _inode *) fp->f_data;
    int err;

    D2(PRINTK("jffs2_fo_getinfo\n"));

    switch (key) {
    case FS_INFO_CONF:
        err = jffs2_pathconf(node, (struct jffs2_pathconf_info *) buf);
        break;

    default:
        err = EINVAL;
    }
    return err;
}

// -------------------------------------------------------------------------
// jffs2_fo_setinfo()
// Set info. Nothing supported here.

static int jffs2_fo_setinfo(struct JFFS2_FILE_TAG *fp, int key, void *buf,
                int len)
{
    // No setinfo key supported at present

    D2(PRINTK("jffs2_fo_setinfo\n"));

    return ENOERR;
}

//==========================================================================
// Directory operations

// -------------------------------------------------------------------------
// jffs2_fo_dirread()
// Read a single directory entry from a file.

static  void filldir(char *nbuf, int nlen, const unsigned char *name, int namlen)
{
    int len = nlen < namlen ? nlen : namlen;
    (void)memcpy_s(nbuf, nlen, name, len);
    nbuf[len] = '\0';
}

static int jffs2_fo_dirread(struct JFFS2_FILE_TAG *fp, struct JFFS2_UIO_TAG *uio)
{
    struct _inode *d_inode = (struct _inode *) fp->f_data;
    struct dirent *ent = (struct dirent *) uio->uio_iov[0].iov_base;
    char *nbuf = ent->d_name;
    int nlen = sizeof (ent->d_name) - 1;
    off_t len = uio->uio_iov[0].iov_len;
    struct jffs2_inode_info *f;
    struct _inode *inode = d_inode;
    struct jffs2_full_dirent *fd;
    unsigned long offset, curofs;
    int found = 1;

    if (len < (off_t)sizeof (struct dirent))
        return EINVAL;

    D1(printk
       (KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", d_inode->i_ino));

    f = JFFS2_INODE_INFO(inode);

    offset = fp->f_offset;
    if (offset == 0) {
        D1(printk
           (KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino));
        filldir(nbuf, nlen, (const unsigned char *) ".", 1);
#ifdef JFFS2PKG_FS_JFFS2_RET_DIRENT_DTYPE
                // Flags here are the same as jffs2_mkdir. Make sure
                // d_type is the same as st_mode of calling stat.
                ent->d_type =
                  jemode_to_cpu(cpu_to_jemode(S_IRUGO|S_IXUGO|S_IWUSR|S_IFDIR));
#endif
                goto out;
    }
    if (offset == 1) {
        filldir(nbuf, nlen, (const unsigned char *) "..", 2);
#ifdef JFFS2PKG_FS_JFFS2_RET_DIRENT_DTYPE
                // Flags here are the same as jffs2_mkdir. Make sure
                // d_type is the same as st_mode of calling stat.
                ent->d_type =
                  jemode_to_cpu(cpu_to_jemode(S_IRUGO|S_IXUGO|S_IWUSR|S_IFDIR));
#endif
                goto out;
    }
    curofs = 1;
    (void)down(&f->sem);
    for (fd = f->dents; fd; fd = fd->next) {
        curofs++;
        /* First loop: curofs = 2; offset = 2 */
        if (curofs < offset) {
            D2(printk
               (KERN_DEBUG
                "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
                fd->name, fd->ino, fd->type, curofs, offset));
            continue;
        }
        if (!fd->ino) {
            D2(printk
               (KERN_DEBUG "Skipping deletion dirent \"%s\"\n",
                fd->name));
            offset++;
            continue;
        }
        D2(printk
           (KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset,
            fd->name, fd->ino, fd->type));
        filldir(nbuf, nlen, fd->name, strlen((char *)fd->name));
        ent->d_type = fd->type;
        goto out_sem;
    }
    /* Reached the end of the directory */
    found = 0;
      out_sem:
    (void)up(&f->sem);
    if(NULL==fd)
    {
        fp->f_offset=2;
        D2(printk
            (KERN_DEBUG "reached the end of the directory\n"));
        return ENOENT;
    }
    out:
    fp->f_offset = ++offset;


    if (found) {
        uio->uio_resid -= sizeof (struct dirent);
    }
    return ENOERR;
}

// -------------------------------------------------------------------------
// jffs2_fo_dirlseek()
// Seek directory to start.

static int jffs2_fo_dirlseek(struct JFFS2_FILE_TAG *fp, off_t * pos, int whence)
{
    // Only allow SEEK_SET to zero
    D2(PRINTK("jffs2_fo_dirlseek\n"));

    if (whence != SEEK_SET || *pos != 0)
        return EINVAL;

    *pos = fp->f_offset = 2;

    return ENOERR;
}

//==========================================================================
//
// Called by JFFS2
// ===============
//
//
//==========================================================================

unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c,
                   struct jffs2_inode_info *f,
                   unsigned long offset,
                   unsigned long *priv)
{
    /* FIXME: This works only with one file system mounted at a time */
    int ret;

    ret = jffs2_read_inode_range(c, f, gc_buffer,
                     offset & ~(PAGE_CACHE_SIZE-1), PAGE_CACHE_SIZE);
    if (ret)
        return ERR_PTR(ret);

    return gc_buffer;
}

void jffs2_gc_release_page(struct jffs2_sb_info *c,
               unsigned char *ptr,
               unsigned long *priv)
{
    /* Do nothing */
}

static struct _inode *new_inode(struct super_block *sb)
{
    struct _inode *inode;
    struct _inode *cached_inode;

    inode = malloc(sizeof (struct _inode));
    if (inode == NULL)
        return 0;

    D2(PRINTK("malloc new_inode %x ####################################\n",
        inode));

    (void)memset_s(inode, sizeof (struct _inode), 0, sizeof (struct _inode));
    inode->i_sb = sb;
    inode->i_ino = 1;
    inode->i_count = 1;
    inode->i_nlink = 1;    // Let JFFS2 manage the link count
    inode->i_size = 0;

    inode->i_cache_next = NULL;    // Newest inode, about to be cached
    // Add to the icache
    for (cached_inode = sb->s_root; cached_inode != NULL;
         cached_inode = cached_inode->i_cache_next) {
        if (cached_inode->i_cache_next == NULL) {
            cached_inode->i_cache_next = inode;    // Current last in cache points to newcomer
            inode->i_cache_prev = cached_inode;    // Newcomer points back to last
            break;
        }
    }
    return inode;
}

static struct _inode *ilookup(struct super_block *sb, jffs2_uint32 ino)
{
    struct _inode *inode = NULL;

    D2(PRINTK("ilookup\n"));
    // Check for this inode in the cache
    jffsfs_Lock();
    for (inode = sb->s_root; inode != NULL; inode = inode->i_cache_next) {
        if (inode->i_ino == ino) {
            inode->i_count++;
            break;
        }
    }
    jffsfs_Unlock();
    return inode;
}

struct _inode *jffs2_iget(struct super_block *sb, jffs2_uint32 ino)
{
    // Called in super.c jffs2_read_super, dir.c jffs2_lookup,
    // and gc.c jffs2_garbage_collect_pass

    // Must first check for cached inode
    // If this fails let new_inode create one

    struct _inode *inode;
    int err;

    D2(PRINTK("jffs2_iget\n"));

    jffsfs_Lock();
    inode = ilookup(sb, ino);
    if (inode) {
        jffsfs_Unlock();
        return inode;
    }

    // Not cached, so malloc it

    inode = new_inode(sb);
    if (inode == NULL) {
        jffsfs_Unlock();
        return ERR_PTR(-ENOMEM);
    }
    inode->i_ino = ino;
    err = jffs2_read_inode(inode);

    if (err) {
        PRINTK("jffs2_read_inode() failed\n");
                inode->i_nlink = 0; // free _this_ bad inode right now

        jffs2_iput(inode);
        jffsfs_Unlock();
        return ERR_PTR(err);
    }

    jffsfs_Unlock();
    return inode;
}

// -------------------------------------------------------------------------
// Decrement the reference count on an inode. If this makes the ref count
// zero, then this inode can be freed.

void jffs2_iput(struct _inode *i)
{
    // Called in jffs2_find
    // (and jffs2_open and jffs2_ops_mkdir?)
    // super.c jffs2_read_super,
    // and gc.c jffs2_garbage_collect_pass
    struct jffs2_inode_info *f = (struct jffs2_inode_info *)NULL;
    struct _inode *temp = (struct _inode *)NULL;
    jffsfs_Lock();
 recurse:
    if (!i) {
        PRINT_ERR("jffs2_iput() called with NULL inode\n");
        // and let it fault...
        jffsfs_Unlock();
        return;
    }
    i->i_count--;
    if (i->i_count < 0)
        BUG();

    if (i->i_count)
    {
        jffsfs_Unlock();
        return;
    }

    if (!i->i_nlink) {
        struct _inode *parent;
        // Remove from the icache linked list and free immediately
        if (i->i_cache_prev)
            i->i_cache_prev->i_cache_next = i->i_cache_next;
        if (i->i_cache_next)
            i->i_cache_next->i_cache_prev = i->i_cache_prev;
        parent = i->i_parent;
        jffs2_clear_inode(i);
        f= JFFS2_INODE_INFO(i);
        (void)destory_MUTEX(&(f->sem));
        (void)memset_s(i, sizeof(*i), 0x5a, sizeof(*i));
        temp = i;
        free(i);

        if (parent && parent != temp) {
            i = parent;
            goto recurse;
        }

    } else {
        // Evict some _other_ inode with i_count zero, leaving
        // this latest one in the cache for a while
        icache_evict(i->i_sb->s_root, i);
    }
    jffsfs_Unlock();
}


// -------------------------------------------------------------------------
// EOF jffs2.c


static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
{
    (void)memset_s(f, sizeof(*f), 0, sizeof(*f));
    init_MUTEX_LOCKED(&f->sem);
}

static void jffs2_clear_inode (struct _inode *inode)
{
        /* We can forget about this inode for now - drop all
         *  the nodelists associated with it, etc.
         */
        struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
        struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);

        D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));

        jffs2_do_clear_inode(c, f);
}


/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
   fill in the raw_inode while you're at it. */
struct _inode *jffs2_new_inode (struct _inode *dir_i, int mode, struct jffs2_raw_inode *ri)
{
    struct _inode *inode;
    struct super_block *sb = dir_i->i_sb;
    struct jffs2_sb_info *c;
    struct jffs2_inode_info *f;
    int ret;

    D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));

    c = JFFS2_SB_INFO(sb);

    jffsfs_Lock();
    inode = new_inode(sb);

    if (!inode) {
        jffsfs_Unlock();
        return ERR_PTR(-ENOMEM);
    }

    f = JFFS2_INODE_INFO(inode);
    jffs2_init_inode_info(f);

    (void)memset_s(ri, sizeof(*ri), 0, sizeof(*ri));
    /* Set OS-specific defaults for new inodes */
    ri->uid = ri->gid = cpu_to_je16(0);
    ri->mode =  cpu_to_jemode(mode);
    ret = jffs2_do_new_inode (c, f, mode, ri);
    if (ret) {
                // forceful evict: f->sem is locked already, and the
                // inode is bad.
                if (inode->i_cache_prev)
                       inode->i_cache_prev->i_cache_next = inode->i_cache_next;
                if (inode->i_cache_next)
                       inode->i_cache_next->i_cache_prev = inode->i_cache_prev;
                (void)up(&(f->sem));
                jffs2_clear_inode(inode);
                (void)destory_MUTEX(&(f->sem));
                (void)memset_s(inode, sizeof(*inode), 0x6a, sizeof(*inode));
                free(inode);
                jffsfs_Unlock();
                return ERR_PTR(ret);
    }
    inode->i_nlink = 1;
    inode->i_ino = je32_to_cpu(ri->ino);
    inode->i_mode = jemode_to_cpu(ri->mode);
    inode->i_gid = je16_to_cpu(ri->gid);
    inode->i_uid = je16_to_cpu(ri->uid);
    inode->i_atime = inode->i_ctime = inode->i_mtime = jffs2_get_timestamp();
    ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime);

    inode->i_size = 0;
    jffsfs_Unlock();

    return inode;
}


static int jffs2_read_inode (struct _inode *inode)
{
    struct jffs2_inode_info *f;
    struct jffs2_sb_info *c;
    struct jffs2_raw_inode latest_node;
    int ret;

    D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));

    f = JFFS2_INODE_INFO(inode);
    c = JFFS2_SB_INFO(inode->i_sb);

    jffs2_init_inode_info(f);

    ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);

    if (ret) {
        (void)up(&f->sem);

        return ret;
    }
    inode->i_mode = jemode_to_cpu(latest_node.mode);
    inode->i_uid = je16_to_cpu(latest_node.uid);
    inode->i_gid = je16_to_cpu(latest_node.gid);
    inode->i_size = je32_to_cpu(latest_node.isize);
    inode->i_atime = je32_to_cpu(latest_node.atime);
    inode->i_mtime = je32_to_cpu(latest_node.mtime);
    inode->i_ctime = je32_to_cpu(latest_node.ctime);

    inode->i_nlink = f->inocache->nlink;
    (void)up(&f->sem);

    D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
    return 0;
}


void jffs2_gc_release_inode(struct jffs2_sb_info *c,
                   struct jffs2_inode_info *f)
{
    jffs2_iput(OFNI_EDONI_2SFFJ(f));
}

struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
                             int inum, int nlink)
{
    struct _inode *inode;
    struct jffs2_inode_cache *ic;

    if (!nlink) {
        /* The inode has zero nlink but its nodes weren't yet marked
           obsolete. This has to be because we're still waiting for
           the final (close() and) jffs2_iput() to happen.

           There's a possibility that the final jffs2_iput() could have
           happened while we were contemplating. In order to ensure
           that we don't cause a new read_inode() (which would fail)
           for the inode in question, we use ilookup() in this case
           instead of jffs2_iget().

           The nlink can't _become_ zero at this point because we're
           holding the alloc_sem, and jffs2_do_unlink() would also
           need that while decrementing nlink on any inode.
        */
        inode = ilookup(OFNI_BS_2SFFJ(c), inum);
        if (!inode) {
            D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
                  inum));

            spin_lock(&c->inocache_lock);
            ic = jffs2_get_ino_cache(c, inum);
            if (!ic) {
                D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
                spin_unlock(&c->inocache_lock);
                return NULL;
            }
            if (ic->state != INO_STATE_CHECKEDABSENT) {
                /* Wait for progress. Don't just loop */
                D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
                      ic->ino, ic->state));
                sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
            } else {
                spin_unlock(&c->inocache_lock);
            }

            return NULL;
        }
    } else {
        /* Inode has links to it still; they're not going away because
           jffs2_do_unlink() would need the alloc_sem and we have it.
           Just jffs2_iget() it, and if read_inode() is necessary that's OK.
        */
        inode = jffs2_iget(OFNI_BS_2SFFJ(c), inum);
        if (IS_ERR(inode))
            return (void *)inode;
    }

    return JFFS2_INODE_INFO(inode);
}

